from Abstract import Argument
class Interface (Argument):
	Token = 'on'
# ----------------------------------------------------------------------------

class IPv4 (Argument):
	from socket import inet_aton, inet_ntoa, error as SocketError
	Token = 'addr'
	Masks = [inet_aton (Mask) for Mask in ('0.0.0.0',
		'128.000.000.000', '192.000.000.000', '224.000.000.000', '240.000.000.000',
		'248.000.000.000', '252.000.000.000', '254.000.000.000', '255.000.000.000',
		'255.128.000.000', '255.192.000.000', '255.224.000.000', '255.240.000.000',
		'255.248.000.000', '255.252.000.000', '255.254.000.000', '255.255.000.000',
		'255.255.128.000', '255.255.192.000', '255.255.224.000', '255.255.240.000',
		'255.255.248.000', '255.255.252.000', '255.255.254.000', '255.255.255.000',
		'255.255.255.128', '255.255.255.192', '255.255.255.224', '255.255.255.240',
		'255.255.255.248', '255.255.255.252', '255.255.255.254', '255.255.255.255',
	)]

	def check_value (self, Value):
		Test = Value.split ('..', 1)
		if len (Test) > 1:
			First, Last = Test
			if not self.is_dotted_quad (First):
				self.fail_check ("First address in a range must be a valid dotted quad, not '%s'" % First)
			if not self.is_dotted_quad (Last):
				self.fail_check ("Last address in a range must be a valid dotted quad, not '%s'" % Last)
			return True
		Test = Value.split ('/', 1)
		if len (Test) > 1:
			Addr, Net = Test
			if not self.is_dotted_quad (Addr):
				self.fail_check ("Address in ip/net form must be a valid dotted quad, not '%s'" % Addr)
			if Net.isdigit ():
				if int (Net) not in xrange (33):
					self.fail_check ("Prefix in ip/net form must be between 0 and 32, not '%s'" % Net)
			else:
				if not self.is_dotted_quad (Net) or self.inet_aton (Net) not in self.Masks:
					self.fail_check ("Invalid netmask '%s'" % Net)
			return True
		return True

	@classmethod
	def quad2int (self, Quad):
		a, b, c, d = (int (Part) for Part in Quad.split ('.'))
		return (a << 24) + (b << 16) + (c << 8) + d

	@classmethod
	def int2quad (self, Int):
		a = (Int >> 24 << 24)
		b = (Int >> 16 << 16) - a
		c = (Int >> 8 << 8) - a - b
		d = Int - a - b - c
		return '%s.%s.%s.%s' % (a >> 24, b >> 16, c >> 8, d)

	@classmethod
	def is_dotted_quad (self, Addr):
		try:
			Addr = self.inet_aton (Addr)
		except self.SocketError as X:
			return False
		else:
			return self.inet_ntoa (Addr)

	@classmethod
	def net2range (self, inAddr):
		if '/' not in inAddr:
			inAddr += '/32'
		Addr, Prefix = inAddr.split ('/', 1)
		if not Prefix.isdigit ():
			Prefix = self.Masks.index (self.inet_aton (Prefix))
		Prefix = int (Prefix)
		binAddr = self.quad2int (Addr)
		Netmask = int (str ('1' * Prefix).ljust (32, '0'), 2)
		Wildcard = int (str ('0' * Prefix).ljust (32, '1'), 2)
		First = binAddr & Netmask
		Last = binAddr | Wildcard
		if Prefix < 31:
			First += 1
			Last -= 1
		elif Prefix > 31:
			return self.int2quad (binAddr)
		return '%s-%s' % (self.int2quad (First), self.int2quad (Last))
# ----------------------------------------------------------------------------

class Ports (Argument):
	Token = 'ports'

	def check_value (self, Value):
		from socket import getservbyname, error as SocketError
		Test = Value.split ('..', 1)
		if len (Test) > 1:
			First, Last = Test
			if not First.isdigit () or int (First) not in xrange (65536):
				self.fail_check ("First port in a range must be an integer between 0 and 65535, not '%s'" % First)
			if not Last.isdigit () or int (Last) not in xrange (65536):
				self.fail_check ("Last port in a range must be an integer between 0 and 65535, not '%s'" % Last)
			return True
		if Value.isdigit ():
			if int (Value) not in xrange (65536):
				self.fail_check ("Port in numeric form must be an integer between 0 and 65535, not '%s'" % Last)
		else:
			try:
				getservbyname (Value)
			except SocketError as X:
				self.fail_check ("port '%s': " % Value + str (X))
# ----------------------------------------------------------------------------

class Protocol (Argument):
	Token = 'proto'

	def check_value (self, Value):
		from socket import getprotobyname, error as SocketError
		if not Value.isdigit ():
			try:
				getprotobyname (Value)
			except SocketError as X:
				self.fail_check ("proto '%s': " % Value + str (X))
# ----------------------------------------------------------------------------

class ICMP_Type (Argument):
	Token = 'type'
	Types = ('any','echo-reply','pong','destination-unreachable','network-unreachable','host-unreachable','protocol-unreachable','port-unreachable','fragmentation-needed','source-route-failed','network-unknown','host-unknown','network-prohibited','host-prohibited','TOS-network-unreachable','TOS-host-unreachable','communication-prohibited','host-precedence-violation','precedence-cutoff','source-quench','redirect','network-redirect','host-redirect','TOS-network-redirect','TOS-host-redirect','echo-request','ping','router-advertisement','router-solicitation','time-exceeded','ttl-exceeded','ttl-zero-during-transit','ttl-zero-during-reassembly','parameter-problem','ip-header-bad','required-option-missing','timestamp-request','timestamp-reply','address-mask-request','address-mask-reply')

	def check_value (self, Value):
		Test = Value.split ('/', 1)
		if len (Test) > 1:
			Type, Code = Test
			if not Type.isdigit () or int (Type) not in xrange (256):
				self.fail_check ("ICMP type in type/code form should be an integer between 0 and 255, not '%s'" % Type)
			if not Code.isdigit () or int (Code) not in xrange (256):
				self.fail_check ("ICMP code should be an integer between 0 and 255, not '%s'" % Code)
			return True
		if Value.isdigit ():
			if int (Value) not in xrange (256):
				self.fail_check ("ICMP type in integer form should be between 0 and 255, not '%s'" % Value)
		else:
			if Value not in self.Types:
				self.fail_check ("ICMP type in literal form '%s' is unknown, valid types are: %s" % (Value, self.Types))
# ----------------------------------------------------------------------------

class State (Argument):
	Token = 'state'
	States = ('invalid', 'new', 'established', 'related')

	def check_value (self, Value):
		if Value not in self.States:
			self.fail_check ("Unknown state '%s', valid states are: %s" % (Value, self.States))
# ----------------------------------------------------------------------------

class Reject_Type (Argument):
	Token = 'with'
	AllowInvert = False
	AllowMultiple = False
	Types = ('net-unreachable', 'host-unreachable', 'port-unreachable', 'proto-unreachable', 'net-prohibited', 'host-prohibited', 'admin-prohibited')

	def check_value (self, Value):
		if Value not in self.Types:
			self.fail_check ("Unknown reject type '%s', valid types are: %s" % (Value, self.Types))
# ----------------------------------------------------------------------------

class Comment (Argument):
	Token = 'comment'
	AllowInvert = False
	AllowMultiple = False
	RawTokens = True
# ----------------------------------------------------------------------------

class Options (Argument):
	Token = 'options'
	AllowInvert = False
	RawTokens = True
# ----------------------------------------------------------------------------
